#ifndef __LOG_SINK_H__
#define __LOG_SINK_H__

#include "../util/utility.hpp"
#include <memory>
#include <fstream>
#include <sstream>
#include <cassert>

// 日志接收器
// 输出到指定位置: 1. 标准输出  2. 指定文件  3. 滚动文件(文件按照时间/大小进行滚动切换)  ...支持扩展(工厂模式)
// 实现思想: 多态(创建和表示分离)

namespace log_system
{
    class LogSink
    {
    public:
        using pointer = std::shared_ptr<LogSink>;
        virtual ~LogSink() {}
        virtual void log(const char *data, size_t length) = 0;
        virtual std::string show() = 0;
    };

    class StdoutLogSink : public LogSink // 标准输出
    {
    public:
        void log(const char *data, size_t length) override
        {
            std::cout.write(data, length);
        }

        std::string show() override { return "StdoutLogSink"; }
    };

    class SpecifyFileLogSink : public LogSink // 指定文件
    {
    public:
        explicit SpecifyFileLogSink() {}

        SpecifyFileLogSink(const std::string &path_name)
            : _path_name(path_name)
        {
            // std::cout << "_path_name: " << _path_name << std::endl;
            // 创建目录
            log_system::utility::File::createDirectory(log_system::utility::File::getPath(_path_name));
            // 创建并打开文件
            _file_stream.open(_path_name, std::ios::binary | std::ios::app);
            assert(_file_stream.is_open());
        }

        void log(const char *data, size_t length) override
        {
            assert(_file_stream.good());
            _file_stream.write(data, length);
            // std::cout << _file_stream.eof() << " " << _file_stream.fail() << " " << _file_stream.bad() << std::endl;
            // 无需关闭文件流_file_stream
        }

        std::string show() override { return "SpecifyFileLogSink"; }

    private:
        std::string _path_name;     // 文件路径
        std::ofstream _file_stream; // 文件流
    };

    class ScrollFileBySizeLogSink : public LogSink // 以大小进行滚动的滚动文件
    {
    public:
        explicit ScrollFileBySizeLogSink() {}

        ScrollFileBySizeLogSink(const std::string &basefile_name, size_t maxsize)
            : _basefile_name(basefile_name), _maxsize(maxsize), _cursize(0), _number(0)
        {
            std::string path_name = createFileName();
            // 创建目录
            log_system::utility::File::createDirectory(log_system::utility::File::getPath(path_name));
            // 创建并打开文件
            _file_stream.open(path_name, std::ios::binary | std::ios::app);
            assert(_file_stream.is_open());
        }

        void log(const char *data, size_t length) override
        {
            if (_cursize >= _maxsize)
            {
                _file_stream.close(); // 关闭原来已经打开的文件
                std::string path_name = createFileName();
                _file_stream.open(path_name, std::ios::binary | std::ios::app);
                assert(_file_stream.is_open());
                _cursize = 0; // 新建文件清零
            }
            _file_stream.write(data, length);
            assert(_file_stream.good());
            _cursize += length; // 当前文件大小需更改
        }

        std::string show() override { return "ScrollFileBySizeLogSink"; }

    private:
        std::string createFileName() // 创建文件(超过最大满足文件大小时)
        {
            // 获取系统时间创建文件扩展名
            time_t time = log_system::utility::Date::getTime();
            struct tm st;
            localtime_r(&time, &st);
            std::stringstream file_name;
            file_name << _basefile_name;
            file_name << (st.tm_year + 1900) << '|' << (st.tm_mon + 1) << '|' << (st.tm_mday) << '-'; // 年月日
            file_name << (st.tm_hour) << '|' << (st.tm_min) << '|' << (st.tm_sec) << '-';             // 时分秒
            file_name << (_number++);                                                                 // 文件序号
            file_name << ".log";                                                                      // 文件后缀名
            return file_name.str();
        }

    private:
        std::string _basefile_name; // 基础文件名(基础文件名+扩展文件名)
        std::ofstream _file_stream; // 文件流
        size_t _maxsize;            // 最大满足文件大小
        size_t _cursize;            // 当前文件大小
        size_t _number;             // 文件序号
    };

    class ScrollFileByTimeLogSink : public LogSink // 以时间进行滚动的滚动文件
    {
    public:
        enum TimeGap
        {
            GAP_SECOND,
            GAP_MINUTE,
            GAP_HOUR,
            GAP_DAY
        };

    public:
        explicit ScrollFileByTimeLogSink() {}

        ScrollFileByTimeLogSink(const std::string &basefile_name, TimeGap time_gap)
            : _basefile_name(basefile_name)
        {
            setGapSize(time_gap);                                                                                                    // 初始化_gap_size
            _current_gap = _gap_size == 1 ? log_system::utility::Date::getTime() : log_system::utility::Date::getTime() % _gap_size; // 当前时间段
            std::string path_name = createFile();
            log_system::utility::File::createDirectory(log_system::utility::File::getPath(path_name)); // 创建目录
            _file_stream.open(path_name, std::ios::binary | std::ios::app);                            // 创建并打开文件
            assert(_file_stream.is_open());
            // std::cout << "_current_gap:" << _current_gap << " _gap_size:" << _gap_size << std::endl;
        }

        void log(const char *data, size_t length) override
        {
            time_t current_time = _gap_size == 1 ? log_system::utility::Date::getTime() - 1 : log_system::utility::Date::getTime() % _gap_size;
            // std::cout << "current_time: " << current_time << std::endl;
            if (current_time == _current_gap) // 当前时间间隔和规定时间间隔不匹配
            {
                _file_stream.close(); // 关闭原来已经打开的文件
                std::string path_name = createFile();
                _file_stream.open(path_name, std::ios::binary | std::ios::app);
                assert(_file_stream.is_open());
                _current_gap = log_system::utility::Date::getTime();
            }
            _file_stream.write(data, length);
            assert(_file_stream.good());
        }

        std::string show() override { return "ScrollFileByTimeLogSink"; }

    private:
        std::string createFile() // 创建文件(超过最大满足文件大小时)
        {
            // 获取系统时间创建文件扩展名
            time_t time = log_system::utility::Date::getTime();
            struct tm st;
            localtime_r(&time, &st);
            std::stringstream file_name;
            file_name << _basefile_name;
            file_name << (st.tm_year + 1900) << '|' << (st.tm_mon + 1) << '|' << (st.tm_mday) << '-'; // 年月日
            file_name << (st.tm_hour) << '|' << (st.tm_min) << '|' << (st.tm_sec);                    // 时分秒
            file_name << ".log";                                                                      // 文件后缀名
            return file_name.str();
        }

        void setGapSize(TimeGap time_gap)
        {
            switch (time_gap)
            {
            case TimeGap::GAP_SECOND:
                _gap_size = 1;
                break;
            case TimeGap::GAP_MINUTE:
                _gap_size = 60;
                break;
            case TimeGap::GAP_HOUR:
                _gap_size = 3600;
                break;
            case TimeGap::GAP_DAY:
                _gap_size = 3600 * 24;
                break;
            default:
                break;
            }
        }

    private:
        std::string _basefile_name; // 基础文件名(基础文件名+扩展文件名)
        std::ofstream _file_stream; // 文件流
        size_t _current_gap;        // 当前时间间隔
        size_t _gap_size;           // 间隔大小(秒为单位)
    };

    // -----------------------------------------------------------------------------------

    // 工厂发放
    class LogSinkFactory
    {
    public:
        template <typename SinkType, typename... Args>
        static LogSink::pointer create(Args &&...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };

} // namespace log_system

#endif // #ifndef __LOG_SINK_H__
